﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using Loki.Utils;

namespace Loki.UI
{
    public class EditableViewModel : ViewModelBase, IDataErrorInfo
    {
        #region Validation

        #region ErrorType

        private static PropertyChangedEventArgs _ErrorTypeChangedArgs = ObservableHelper.CreateChangedArgs<EditableViewModel>(x => x.ErrorType);

        private static PropertyChangingEventArgs _ErrorTypeChangingArgs = ObservableHelper.CreateChangingArgs<EditableViewModel>(x => x.ErrorType);

        private ValidationMode _ErrorType;

        public ValidationMode ErrorType
        {
            get
            {
                return _ErrorType;
            }

            set
            {
                if (value != _ErrorType)
                {
                    NotifyChanging(_ErrorTypeChangingArgs);
                    _ErrorType = value;
                    NotifyChanged(_ErrorTypeChangedArgs);
                }
            }
        }

        #endregion ErrorType

        #region IsValid

        private static PropertyChangedEventArgs _IsValidChangedArgs = ObservableHelper.CreateChangedArgs<EditableViewModel>(x => x.IsValid);

        private static PropertyChangingEventArgs _IsValidChangingArgs = ObservableHelper.CreateChangingArgs<EditableViewModel>(x => x.IsValid);

        private bool _IsValid;

        public bool IsValid
        {
            get
            {
                return _IsValid;
            }

            set
            {
                if (value != _IsValid)
                {
                    NotifyChanging(_IsValidChangingArgs);
                    _IsValid = value;
                    NotifyChanged(_IsValidChangedArgs);
                }
            }
        }

        #endregion IsValid

        #region ValidationEnabled

        private static PropertyChangedEventArgs _ValidationEnabledChangedArgs = ObservableHelper.CreateChangedArgs<EditableViewModel>(x => x.ValidationEnabled);

        private static PropertyChangingEventArgs _ValidationEnabledChangingArgs = ObservableHelper.CreateChangingArgs<EditableViewModel>(x => x.ValidationEnabled);

        private bool _ValidationEnabled;

        public bool ValidationEnabled
        {
            get
            {
                return _ValidationEnabled;
            }

            set
            {
                if (value != _ValidationEnabled)
                {
                    NotifyChanging(_ValidationEnabledChangingArgs);
                    _ValidationEnabled = value;
                    NotifyChanged(_ValidationEnabledChangedArgs);
                }
            }
        }

        #endregion ValidationEnabled

        private ConcurrentDictionary<string, IValidator> _Validators = new ConcurrentDictionary<string, IValidator>();

        public string Error
        {
            get { return string.Join(Environment.NewLine, _Validators.Where(x => !x.Value.Validate()).Select(x => x.Value.GetErrorMessage())); }
        }

        public IDictionary<string, IValidator> Validators
        {
            get
            {
                return _Validators;
            }
        }

        public string this[string columnName]
        {
            get
            {
                if (_Validators.ContainsKey(columnName))
                {
                    return _Validators[columnName].GetErrorMessage();
                }
                else
                {
                    return string.Empty;
                }
            }
        }

        public bool Validate()
        {
            return Validate(ValidationMode.Error);
        }

        public virtual bool Validate(ValidationMode P_Mode)
        {
            ErrorType = P_Mode;
            IsValid = string.IsNullOrWhiteSpace(Error);
            return IsValid;
        }

        protected void AddValidator<TViewModel, TProperty>(Expression<Func<TViewModel, TProperty>> P_Getter, IValidationRule P_Rule, string P_ErrorMessage) where TViewModel : class, IDataErrorInfo
        {
            string L_PropertyName = ExpressionHelper.GetProperty(P_Getter).Name;
            Func<TViewModel, TProperty> L_GenericGetter = P_Getter.Compile();
            TViewModel L_ConcreteModel = this as TViewModel;

            Validator<TProperty> L_Validator = new Validator<TProperty>(() => L_GenericGetter(L_ConcreteModel));
            L_Validator.AddRule(P_Rule, P_ErrorMessage);

            _Validators.AddOrUpdate(L_PropertyName, L_Validator, (k, v) => { v.AddRule(P_Rule, P_ErrorMessage); return v; });
        }

        #endregion Validation
    }
}